import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.GregorianCalendar;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/** Main GUI class for the STP Simulator
    CS158A Spring 2010 Project #2
    @author Clare Lanam and Loc Phan
    */

public class STP_Simulator extends JFrame implements ChangeListener
{
   /** panel in which to display bridges & LANs */
   private ClickyPanel display = new ClickyPanel();
   /** panel for detailed status info */
   private StatusBox statusBox = new StatusBox();
   
   /** serial ID added by Eclipse compiler */
   private static final long serialVersionUID = 1L;
   
   // ------[MENU ITEMS]------ //
   
   /** menu bar */
   private JMenuBar menuBar = new JMenuBar();
   
   /** file menu */
   private JMenu jmFile = new JMenu("File");
   /** menu item: start a new LAN */
   private JMenuItem jmiNew = new JMenuItem("New (Start Over)");
   /** menu item: load a saved LAN */
   private JMenuItem jmiLoad = new JMenuItem("Load Configuration");
   /** menu item: save the current LAN */
   private JMenuItem jmiSave = new JMenuItem("Save Configuration");
   /** menu item: quit */
   private JMenuItem jmiQuit = new JMenuItem("Quit");
   
   /** action menu */
   private JMenu jmAction = new JMenu("Action");
   /** menu item: start & stop simulation */
   private JMenuItem jmiBegin = new JMenuItem("Begin Simulation");
   /** menu item: (un)link selected bridges */
   private JMenuItem jmiLinkSelected = new JMenuItem("");
   /** enable selected bridges */
   private JMenuItem jmiEnableSelected = new JMenuItem("Enable Selected");
   /** disable selected bridges */
   private JMenuItem jmiDisableSelected = new JMenuItem("Disable Selected");
   /** menu item: delete selected bridge */
   private JMenuItem jmiDelete = new JMenuItem("Delete Bridge");
   
   // ------[MISCELLANEOUS VARIABLES]------ //
   
   /** current state -- link or unlink? */
   private boolean actionIsLink;
   /** time of last action  in milliseconds */
   private long timeOfLastAction = new GregorianCalendar().getTimeInMillis();
   /** title for title bar */
   private static final String titleBar = "CS158A ~ STP Simulation ";

   // ------[CONSTRUCTOR]------ //
   
   /** Constructor for the frame. */
   
   public STP_Simulator()
   {
      super("STP Simulation ~ CS158A");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      
      // ------:set up menu bar:------
      
      jmFile.setMnemonic('F');
      jmiNew.setMnemonic('N');
      jmiLoad.setMnemonic('L');
      jmiSave.setMnemonic('S');
      jmiQuit.setMnemonic('Q');
      
      jmAction.setMnemonic('A');
      jmiBegin.setMnemonic('B');
      jmiLinkSelected.setMnemonic('L');
      jmiEnableSelected.setMnemonic('E');
      jmiDelete.setMnemonic('D');
      
      jmiNew.setAccelerator(KeyStroke.getKeyStroke
            (KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK));
      jmiLoad.setAccelerator(KeyStroke.getKeyStroke
            (KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK));
      jmiSave.setAccelerator(KeyStroke.getKeyStroke
            (KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
      jmiQuit.setAccelerator(KeyStroke.getKeyStroke
            (KeyEvent.VK_Q, InputEvent.CTRL_DOWN_MASK));
      
      jmiBegin.setAccelerator(KeyStroke.getKeyStroke
            (KeyEvent.VK_B, InputEvent.CTRL_DOWN_MASK));
      jmiLinkSelected.setAccelerator(KeyStroke.getKeyStroke
            (KeyEvent.VK_L, InputEvent.CTRL_DOWN_MASK));
      jmiEnableSelected.setAccelerator(KeyStroke.getKeyStroke
            (KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK));
      jmiDisableSelected.setAccelerator(KeyStroke.getKeyStroke
            (KeyEvent.VK_D, InputEvent.CTRL_DOWN_MASK));
      jmiDelete.setAccelerator(KeyStroke.getKeyStroke
            (KeyEvent.VK_X, InputEvent.CTRL_DOWN_MASK));
      
      jmFile.add(jmiNew);
      jmFile.add(jmiLoad);
      jmFile.add(jmiSave);
      jmFile.add(jmiQuit);
      
      jmAction.add(jmiBegin);
      jmAction.add(jmiLinkSelected);
      jmAction.add(jmiEnableSelected);
      jmAction.add(jmiDisableSelected);
      jmAction.add(jmiDelete);
      
      menuBar.add(jmFile);
      menuBar.add(jmAction);
      setJMenuBar(menuBar);
  
      jmiNew.addActionListener(new alNew());
      jmiLoad.addActionListener(new alLoad());
      jmiSave.addActionListener(new alSave());
      
      jmiBegin.addActionListener(new alStartSim());
      jmiLinkSelected.addActionListener(new alLinkSelected());
      jmiEnableSelected.addActionListener(new alEnableSelected());
      jmiDisableSelected.addActionListener(new alDisableSelected());
      jmiDelete.addActionListener(new alDeleteSelected());
      
      jmiQuit.addActionListener(new ActionListener() 
      {
         public void actionPerformed(ActionEvent arg0) 
         {
            display.clearData();
            System.exit(0);      
         }
      });
      
      // ------:make a scroll pane for the status box:------
      
      JScrollPane jspStatus = new JScrollPane(statusBox);
      jspStatus.setHorizontalScrollBarPolicy(
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
      jspStatus.setVerticalScrollBarPolicy(
            JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
      jspStatus.setPreferredSize(
            new Dimension(ClickyPanel.PANEL_WIDTH,150));
      jspStatus.setBorder(null);
      jspStatus.setFocusable(false);
      
      // ------:add panels to the frame:------
      
      setLocation(100, 100); // psh! :p
      setLayout(new BorderLayout());
      add(display, BorderLayout.CENTER);
      add(jspStatus, BorderLayout.SOUTH);
      
      // ------:action listener for the timer:------
      
      ActionListener alTimer = new ActionListener()
      {
         public void actionPerformed(ActionEvent event)
         {
            if (!display.isRunning())
            {
               setTitle(titleBar);
               return;
            }
            
            long now = new GregorianCalendar().getTimeInMillis();
            long elapsed = now - timeOfLastAction;
            int seconds = (int)(elapsed / 1000L) % 60;
            int minutes = (int)(elapsed / 1000000L) % 60;
            int hours = (int)(elapsed / 1000000000L) % 60;
            String time = String.format("%1$02d:%2$02d:%3$02d",
                  hours, minutes, seconds);
            
            setTitle(titleBar + time);
         }
      };

      // ------:update title bar every DELAY milliseconds:------
      
      final int DELAY = 100;
      Timer t = new Timer(DELAY, alTimer);
      t.start();
      
      // ------:final details:------
      updateControls();
      display.attach(this);
      pack();
   }
   
   // ------[CHANGE LISTENER IMPLEMENTATION]------ //
   
   /** Updates controls when the ClickyPanel changes. */ 
   
   public void stateChanged(ChangeEvent c)
   {
      updateControls();
   }
   
   // ------[PRIVATE HELPER METHODS]------ //
   
   /** ActionListioner for deleting the selected bridge. */
   
   private class alDeleteSelected implements ActionListener
   {
      public void actionPerformed(ActionEvent e)
      {
         if (display.getNumberOfSelectedBridges() != 1)
            return;
         
         display.deleteSelectedBridge();
         timeOfLastAction = new GregorianCalendar().getTimeInMillis();
      }
   }
   
   /** ActionListioner for enabling selected bridges. */
   
   private class alEnableSelected implements ActionListener
   {
      public void actionPerformed(ActionEvent e)
      {
         display.enableSelected();
         timeOfLastAction = new GregorianCalendar().getTimeInMillis();
      }
   }
   
   /** ActionListioner for deleting the selected bridge. */
   
   private class alDisableSelected implements ActionListener
   {
      public void actionPerformed(ActionEvent e)
      {
         display.disableSelected();
         timeOfLastAction = new GregorianCalendar().getTimeInMillis();
      }
   }
   
   /** ActionListioner for (un)linking the selected bridges. */
   
   private class alLinkSelected implements ActionListener
   {
      public void actionPerformed(ActionEvent e)
      {
         if (actionIsLink)
            display.linkSelected();
         else
            display.unlinkSelected();
         timeOfLastAction = new GregorianCalendar().getTimeInMillis();
      }
   }
   
   /** ActionListioner for loading a configuration of bridges. */
   
   private class alLoad implements ActionListener
   {
      public void actionPerformed(ActionEvent e)
      {
         display.load();
         timeOfLastAction = new GregorianCalendar().getTimeInMillis();
      }
   }
   
   /** ActionListioner for clearing the configuration. */
   
   private class alNew implements ActionListener
   {
      public void actionPerformed(ActionEvent e)
      {
         display.clearData();
         timeOfLastAction = new GregorianCalendar().getTimeInMillis();
      }
   }
   
   /** ActionListioner for saving a configuration of bridges. */
   
   private class alSave implements ActionListener
   {
      public void actionPerformed(ActionEvent e)
      {
         display.save();
      }
   }
   
   /** ActionListioner for starting/stopping the simulation. */
   
   private class alStartSim implements ActionListener
   {
      public void actionPerformed(ActionEvent e)
      {
         display.run();
         timeOfLastAction = new GregorianCalendar().getTimeInMillis();
      }
   }
   
   /** Updates user controls. */
   
   private void updateControls()
   {
      // ------:update the link/unlink menu item:------
      
      int numBridgesSelected = display.getNumberOfSelectedBridges();
      jmiLinkSelected.setEnabled(numBridgesSelected == 2);
      jmiDelete.setEnabled(numBridgesSelected == 1);
      
      int numBridges = display.getNumberOfBridges();
      jmiBegin.setEnabled((numBridges > 0) && !display.isRunning());
      jmiSave.setEnabled(numBridges > 0);
      
      jmiEnableSelected.setEnabled(display.canEnableSelected());
      jmiDisableSelected.setEnabled(display.canDisableSelected());
      
      if (display.selectedBridgesAreLinked())
      {
         actionIsLink = false;
         jmiLinkSelected.setText("Un-Link Selected Bridges");
      }
      else
      {
         actionIsLink = true;
         jmiLinkSelected.setText("Link Selected Bridges");
      }
      
      // ------:update status box too:------
      
      if (numBridgesSelected == 1)
         statusBox.showBridgeStatus(display.getSelectedBridgeA());
      else if (numBridgesSelected == 2)
         statusBox.showLinkStatus(display.getSelectedBridgeA(),
               display.getSelectedBridgeB(), !actionIsLink);
      else
         statusBox.clear();
   }
   
   // ------[MAIN METHOD]------ //
   
   /** Run GUI from here. */
   
   public static void main(String[] args)
   {
      STP_Simulator wombat = new STP_Simulator();
      wombat.setVisible(true);
   }
}
